package com.company.project.wechat.weChatPay.service.impl;

import com.alibaba.nacos.api.config.annotation.NacosValue;
import com.company.project.wechat.weChatPay.service.WeChatPayService;
import com.company.project.wechat.commom.WxUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * 第三方支付回调接口
 * @author ZYF
 * @date 2021/5/18 11:03
 */
@Service
@Slf4j
public class WeChatPayServiceImpl implements WeChatPayService {

    //SSL证书路径
    @NacosValue(value = "${wxpay.filePath:1}",autoRefreshed = true)
    private String filePath;

    @NacosValue(value = "${wxpay.certPassword:1}",autoRefreshed = true)
    private String certPassword;

    @NacosValue(value = "${wxpay.appid:1}",autoRefreshed = true)
    private String appid;

    //商户号
    @NacosValue(value = "${wxpay.mchid:1}",autoRefreshed = true)
    private String mch_id;

    @NacosValue(value = "${wxpay.notify_url:1}",autoRefreshed = true)
    private String notifyUrl;

    @NacosValue(value = "${wxpay.key:1}",autoRefreshed = true)
    private String key;

    @Autowired
    private WxUtils wxUtils;


    // 下单 API 地址
    private static String placeUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";

    //申请退款 API 地址
    private static String refundUrl = "https://api.mch.weixin.qq.com/secapi/pay/refund";

    //交易账单 API 地址
    private static String billUrl = "https://api.mch.weixin.qq.com/pay/downloadbill";

    /**
     *支付统一下单
     * @param orderId   公司业务订单号
     * @param price     价格
     * @param body      主题信息
     * @param ipAddress 客户端APP IP 地址
     * @return
     * @throws IOException
     */
    @Override
    public Map createOrder(String orderId, BigDecimal price, String body, String ipAddress) throws IOException {
        SortedMap<String, Object> parameters = new TreeMap<String, Object>();
        parameters.put("appid", appid);
        parameters.put("mch_id", mch_id);
        parameters.put("device_info", "WEB"); // 默认 "WEB"
        parameters.put("body", body);
        parameters.put("nonce_str", wxUtils.gen32RandomString()); // 32 位随机字符串
        parameters.put("notify_url", notifyUrl);
        parameters.put("out_trade_no", orderId);
        parameters.put("total_fee", price.multiply(BigDecimal.valueOf(100)).intValue());
        parameters.put("spbill_create_ip", ipAddress);
        parameters.put("trade_type", "APP");
        parameters.put("sign", wxUtils.createSign(parameters, key)); // sign 必须在最后
        String result = wxUtils.executeHttpPost(placeUrl, parameters); // 执行 HTTP 请求，获取接收的字符串（一段 XML）
        return wxUtils.createSign2(result, key);
    }

    /**
     * 微信小程序退款订单(需要双向证书) 注：商户订单号，商户退款单号必须二选一
     * @param transactionId 微信支付订单号
     * @param outTradeNo 商户订单号
     * @param outRefundNo 商户退款单号
     * @param totalFee 订单金额
     * @param refundFee 退款金额
     * @param refundDesc 退款原因(可无)
     * @param notifyUrl 退款结果通知url(可无)
     * @return
     * @throws Exception
     */
    @Override
    public String doRefundRequest(String transactionId,String outTradeNo,String outRefundNo,BigDecimal totalFee,BigDecimal refundFee,String refundDesc,String notifyUrl) throws Exception {
        SortedMap<String, Object> parameters = new TreeMap<String, Object>();
        parameters.put("appid", appid);
        parameters.put("mch_id", mch_id);
        parameters.put("out_trade_no", outTradeNo); //商户系统内部订单号
        parameters.put("transaction_id", transactionId); //微信生成的订单号，在支付通知中有返回
        parameters.put("nonce_str", wxUtils.gen32RandomString()); // 32 位随机字符串
        parameters.put("out_refund_no", outRefundNo);//商户系统内部的退款单号，商户系统内部唯一
        parameters.put("total_fee", totalFee.multiply(BigDecimal.valueOf(100)).intValue()); //订单金额
        parameters.put("refund_fee", refundFee.multiply(BigDecimal.valueOf(100)).intValue());
        parameters.put("refund_desc", refundDesc);//退款原因
        parameters.put("notify_url", notifyUrl);//退款通知回调
        parameters.put("sign", wxUtils.createSign(parameters, key)); // sign 必须在最后
        //小程序退款需要调用双向证书的认证
        CloseableHttpClient httpClient = wxUtils.readCertificate(filePath, certPassword);
        try {
            HttpPost httpost = new HttpPost(refundUrl); // 设置响应头信息
            httpost.addHeader("Connection", "keep-alive");
            httpost.addHeader("Accept", "*/*");
            httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
            httpost.addHeader("Host", "api.mch.weixin.qq.com");
            httpost.addHeader("X-Requested-With", "XMLHttpRequest");
            httpost.addHeader("Cache-Control", "max-age=0");
            httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
            httpost.setEntity(new StringEntity(wxUtils.transferMapToXml(parameters), "UTF-8"));
            CloseableHttpResponse response = httpClient.execute(httpost);
            try {
                // 预先设定返回的 response 类型为 xml
                response.setHeader("Content-type", "application/xml");
                HttpEntity entity = response.getEntity();
                String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");
                // 读取参数，解析Xml为map
                Map<String, String> map = wxUtils.transferXmlToMap(jsonStr);
                if (map.get("return_code").equals("SUCCESS")) {
                    //这里不做先不做业务逻辑商业处理
                    if (map.get("result_code").equals("SUCCESS")) {
                        log.info("退款成功");
                        return "退款成功" ;
                    } else {
                        String errCode = map.get("err_code");
                        log.error("退款失败,失败原因 = " + errCode);
                        return "退款失败,失败原因 = " + errCode;
                    }
                }
                EntityUtils.consume(entity);
                //这里先不做逻辑处理，根据返回的json做逻辑处理
                return jsonStr;
            } finally {
                response.close();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            httpClient.close();
        }
    }

    /**
     * 微信小程序交易账单
     * @param billDate 对账单日期
     * @return
     * @throws Exception
     */
    @Override
    public  String doDownloadbill(String billDate) throws Exception{
        SortedMap<String, Object> parameters = new TreeMap<String, Object>();
        parameters.put("appid", appid);
        parameters.put("mch_id", mch_id);
        parameters.put("nonce_str", wxUtils.gen32RandomString()); // 32 位随机字符串
        parameters.put("bill_date", billDate); //对账单日期 格式：20140603
        parameters.put("sign", wxUtils.createSign(parameters, key)); // sign 必须在最后
        CloseableHttpClient httpClient = wxUtils.notReadCertificate();
        try {
            HttpPost httpost = new HttpPost(billUrl); // 设置响应头信息
            httpost.addHeader("Connection", "keep-alive");
            httpost.addHeader("Accept", "*/*");
            httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
            httpost.addHeader("Host", "api.mch.weixin.qq.com");
            httpost.addHeader("X-Requested-With", "XMLHttpRequest");
            httpost.addHeader("Cache-Control", "max-age=0");
            httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
            httpost.setEntity(new StringEntity(wxUtils.transferMapToXml(parameters), "UTF-8"));
            CloseableHttpResponse response = httpClient.execute(httpost);
            try {
                response.setHeader("Content-type", "application/xml");
                HttpEntity entity = response.getEntity();
                String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");
                EntityUtils.consume(entity);
                Map<String, String> map = wxUtils.transferXmlToMap(jsonStr);
                if (map.get("return_code").equals("SUCCESS")) {
                    //这里不做先不做业务逻辑商业处理
                    if (map.get("result_code").equals("SUCCESS")) {
                        log.info("交易账单下载成功");
                        return "退款成功";
                    } else {
                        String  errCode = map.get("err_code");
                        log.error("交易账单下载失败,失败原因 = "+errCode);
                        return  "交易账单下载失败,失败原因 = "+errCode;
                    }
                }
                return  jsonStr;
            } finally {
                response.close();
            }
        } catch (Exception e){
            throw new RuntimeException(e);
        } finally {
            httpClient.close();
        }
    }

    /**
     * 支付接口回调（微信调用）
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    @Transactional
    @Override
    public String callBack(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 预先设定返回的 response 类型为 xml
        response.setHeader("Content-type", "application/xml");
        // 读取参数，解析Xml为map
        Map<String, String> map = wxUtils.transferXmlToMap(wxUtils.readRequest(request));
        // 转换为有序 map，判断签名是否正确
        boolean isSignSuccess = wxUtils.checkSign(new TreeMap<String, Object>(map), key);
        if (isSignSuccess) {
            // 签名校验成功，说明是微信服务器发出的数据
            String orderId = map.get("out_trade_no");
            //这里可以根据自己业务订单来查询订单是否被处理过
//            if (tradeService.hasProcessed(orderId)) // 判断该订单是否已经被接收处理过
//                return success();
            // 可在此持久化微信传回的该 map 数据
            //..
            if (map.get("return_code").equals("SUCCESS")) {
                if (map.get("result_code").equals("SUCCESS")) {
                    log.info("支付成功,orderId = "+orderId);
//                    orderService.finishOrder(orderId);  // 支付成功
                } else {
                    log.error("支付失败,orderId = "+orderId);
//                    orderService.failOrder(orderId);    // 支付失败
                }
            }
            return success();
        } else {
            // 签名校验失败（可能不是微信服务器发出的数据）
            return fail();
        }
    }

    /**
     * 账单退款通知接口回调（微信调用）
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    @Transactional
    @Override
    public String wxRequestBack(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 预先设定返回的 response 类型为 xml
        response.setHeader("Content-type", "application/xml");
        // 读取参数，解析Xml为map
        Map<String, String> map = wxUtils.transferXmlToMap(wxUtils.readRequest(request));
            if (map.get("return_code").equals("SUCCESS")) {
                if (map.get("result_code").equals("SUCCESS")) {

                    log.info("退款通知已接收完成");
                } else {
                    String msg = map.get("return_msg");
                    log.error("退款通知接收错误："+msg);
                }
            }
            return success();
    }

    String fail() {
        return "<xml>\n" +
                "  <return_code><![CDATA[FAIL]]></return_code>\n" +
                "  <return_msg><![CDATA[]]></return_msg>\n" +
                "</xml>";
    }

    String success() {
        return "<xml>\n" +
                "  <return_code><![CDATA[SUCCESS]]></return_code>\n" +
                "  <return_msg><![CDATA[OK]]></return_msg>\n" +
                "</xml>";
    }








}
